#include "Inliner.h"
#include <vector>
#include <set>
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/BasicBlock.h"
#include "llvm/Instruction.h"
#include "llvm/Type.h"
#include "llvm/DerivedTypes.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/Instructions.h"
#include "llvm/Module.h"
#include "llvm/Function.h"
#include "llvm/BasicBlock.h"
#include "llvm/Instruction.h"
#include "llvm/Type.h"
#include "llvm/DerivedTypes.h"
#include "llvm/ExecutionEngine/JIT.h"
#include "llvm/Instructions.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
#include "Utils.h"
#include "llvm/Transforms/Utils/Cloning.h"
//#include <boost/foreach.hpp>

using namespace std;
using namespace llvm;

static void FindRecursiveFuncs(Module *M, set<Function*> &RecursiveFuncs) {
  for (Module::iterator MI = M->begin(), ME = M->end(); MI != ME; ++MI) {
    if (MI->isDeclaration() || MI->getNameStr()[0] == '_')
			continue;
		for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI)
			for (BasicBlock::iterator BBI = FI->begin(), BBE = FI->end(); BBI != BBE; ++BBI)
				if (CallInst *CI = dyn_cast<CallInst>(&*BBI))
					if (&*MI == CI->getCalledFunction())
						RecursiveFuncs.insert(&*MI);
  }
}

void Inline(Module *M) {
	//errs() << "in Inline\n";
	set<Function*> RecursiveFuncs;
	FindRecursiveFuncs(M, RecursiveFuncs);

  for (Module::iterator MI = M->begin(), ME = M->end(); MI != ME; ++MI) {
    if (MI->isDeclaration() || MI->getNameStr()[0] == '_')
			continue;

		for (;;) {
			CallInst *InlineThis = 0;
			for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI)
				for (BasicBlock::iterator BBI = FI->begin(), BBE = FI->end(); BBI != BBE; ++BBI)
					if (CallInst *CI = dyn_cast<CallInst>(&*BBI))
						if (Function *CF = CI->getCalledFunction())
							if (!CF->isDeclaration() && CF->getNameStr()[0] != '_' && RecursiveFuncs.find(CF) == RecursiveFuncs.end())
								InlineThis = CI;
			if (!InlineThis)
				break;
//			errs() << "  inlining " << *InlineThis << " in " << MI->getName() << "\n";
			InlineFunctionInfo IFI;
			InlineFunction(InlineThis, IFI);
		}
  }
}
